package com.iteaj.iot.server.udp;

import com.iteaj.iot.CoreConst;
import com.iteaj.iot.FrameworkManager;
import com.iteaj.iot.codec.filter.CombinedInterceptor;
import com.iteaj.iot.config.ConnectProperties;
import com.iteaj.iot.event.ClientStatus;
import com.iteaj.iot.event.StatusEvent;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.timeout.IdleState;
import io.netty.util.concurrent.ScheduledFuture;
import io.netty.util.internal.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.TimeUnit;

@ChannelHandler.Sharable
public class UdpEventManagerHandler extends ChannelDuplexHandler {

    private static UdpEventManagerHandler instance = new UdpEventManagerHandler();
    private Logger logger = LoggerFactory.getLogger(getClass());

    protected UdpEventManagerHandler() { }

    public static UdpEventManagerHandler getInstance() {
        return instance;
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if(msg instanceof UdpServerMessage) {
            UdpServerMessage message = (UdpServerMessage) msg;
            String equipCode = message.getHead().getEquipCode();
            UdpServerComponent serverComponent = (UdpServerComponent)ctx.channel().attr(CoreConst.COMPONENT).get();
            ConnectProperties config = serverComponent.config();
            // 解析出设备编号, 并且指定了读空闲时间 则启用设备注册机制
            if(!StringUtil.isNullOrEmpty(equipCode) && config.getReaderIdleTime() > 0) {
                UdpIdleState idleState = serverComponent.getDeviceManager().find(equipCode);

                // 设备还没注册
                if(idleState == null) {

                    long currentTimeMillis = System.currentTimeMillis();
                    idleState = new UdpIdleState(equipCode, currentTimeMillis, config, message.getSender());
                    serverComponent.getDeviceManager().add(equipCode, idleState);

                    ScheduledFuture<?> schedule = ctx.executor()
                            .schedule(new IdleTask(idleState, ctx), config.getReaderIdleTime(), TimeUnit.SECONDS);
                    idleState.setScheduledFuture(schedule);

                    if(logger.isDebugEnabled()) {
                        logger.debug("客户端上线({}) - 客户端编号: {} - 客户端地址: {}", serverComponent.getName(), equipCode, message.getSender());
                    }

                    //触发设备上线事件
                    FrameworkManager.publishEvent(new StatusEvent(equipCode, ClientStatus.online, serverComponent));
                } else {
                    // 重新更新发送地址
                    idleState.setAddress(message.getSender());
                    idleState.setLastOfMills(System.currentTimeMillis());

                    // 更新定时任务
                    if(config.getReaderIdleTime() > 0) {
                        ScheduledFuture future = idleState.getScheduledFuture();
                        if(future == null) { // 定时任务没执行直接取消
                            ScheduledFuture<?> schedule = ctx.executor()
                                    .schedule(new IdleTask(idleState, ctx), config.getReaderIdleTime(), TimeUnit.SECONDS);
                            idleState.setScheduledFuture(schedule);
                        }
                    }
                }
            } else {

            }
        }

        ctx.fireChannelRead(msg);
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if(evt instanceof UdpIdleState) {
            UdpServerComponent serverComponent = (UdpServerComponent)ctx.channel().attr(CoreConst.COMPONENT).get();
            Object idle = serverComponent.idle(((UdpIdleState) evt).getEquipCode(), IdleState.READER_IDLE);
            if(idle instanceof IdleState) { // 直接下线设备
                UdpIdleState remove = serverComponent.getDeviceManager().remove(((UdpIdleState) evt).getEquipCode());
                if(logger.isDebugEnabled() && remove != null) {
                    logger.debug("客户端超时下线({}) - 客户端编号: {} - 客户端地址: {} - 状态: 下线成功"
                            , serverComponent.getName(), remove.getEquipCode(), remove.getAddress());
                }

                if(remove != null) {
                    //触发设备下线事件
                    FrameworkManager.publishEvent(new StatusEvent(remove.getEquipCode(), ClientStatus.offline, serverComponent));
                }
            } else if(idle != null) { // 写出报文
                String equipCode = ((UdpIdleState) evt).getEquipCode();
                serverComponent.writeAndFlush(equipCode, idle);
            } else {

            }
        }

        super.userEventTriggered(ctx, evt);
    }

    protected class IdleTask implements Runnable {

        private UdpIdleState idleState;
        private ChannelHandlerContext ctx;

        public IdleTask(UdpIdleState idleState, ChannelHandlerContext ctx) {
            this.ctx = ctx;
            this.idleState = idleState;
        }

        @Override
        public void run() {
            try {
                ConnectProperties config = idleState.getConfig();
                // 计算和最后一次读到得时间过去了多少秒
                long seconds = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis() - idleState.getLastOfMills());
                // 流失的时间大于配置得读取得空闲时间
                // 触发超时
                if(seconds >= config.getReaderIdleTime()) {
                    idleState.setScheduledFuture(null);
                    UdpEventManagerHandler.this.userEventTriggered(this.ctx, idleState);
                } else {
                    // 重新创建一个定时任务
                    ScheduledFuture<?> schedule = ctx.executor().schedule(this
                            , config.getReaderIdleTime() - seconds, TimeUnit.SECONDS);
                    idleState.setScheduledFuture(schedule);
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
